package dsproject;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;

public class Network {

    private float X;
    private float Y;
    private float wifiRange;
    public Node nodes[];
    private int currentId = 0;

    public Network(float x, float y, float WR, int N) {
        this.X = x;
        this.Y = y;
        this.wifiRange = WR;
        this.setNodes(N);
    }

    public Path shortestPath(Node source, Node destination) {
        final int maxi = nodes.length;

        int mini;
        int clusterHeadCheck = 0;
        int shift = 0;

        HashMap<Node, Integer> traveled = new HashMap<Node, Integer>();
        HashMap<Node, Node> previous = new HashMap<Node, Node>();

        Node currentNode;

        ArrayList<Node> todo, temp;
        ArrayList<Node> path = new ArrayList<Node>();
        ArrayList<Node> neighboors = new ArrayList<Node>();

        //Initialization of traveled and previous maps
        for (int i = 0; i < nodes.length; i++) {
            traveled.put(nodes[i], maxi);
            previous.put(nodes[i], null);
        }
        todo = new ArrayList<Node>(traveled.keySet());
        traveled.put(source, 0);

        //Main Loop
        while (todo.size() > 0) {

            currentNode = null;
            neighboors.clear();

            //Get the minimum of traveled
            mini = maxi;
            for (Node nd : todo) {
                if (traveled.get(nd) <= mini) {
                    mini = traveled.get(nd);
                    currentNode = nd;
                }
            }
            //remove the current node from the todo list
            todo.remove(currentNode);

            // If the current node is a shortest way to go the son, modify this way
            for (Node nd : getNeighboors(currentNode)) {
                if (traveled.get(nd) > (traveled.get(currentNode) + 1)) {
                    traveled.put(nd, traveled.get(currentNode) + 1);
                    previous.put(nd, currentNode);
                }
            }
        }

        //If there is no path, return null
        if (previous.get(destination) == null) {
            return null;
        }

        // Build the path
        currentNode = destination;
        while (currentNode != source) {
            currentNode = previous.get(currentNode);
            path.add(currentNode);
        }
        path.add(0, destination);
        Collections.reverse(path);

        //Enforce the cluster head transit
        temp = new ArrayList<Node>(path);
        for (int i = 0; i < path.size(); i++) {
            //Check whether the path go through head-clusters
            if (path.get(i).getClStatus() == Node.STATUS.HEAD || clusterHeadCheck == -1) {
                clusterHeadCheck = -1;
            } else {
                clusterHeadCheck++;
            }
            //If we change the cluster without having been to head of this cluster, add it
            if ((i == path.size() - 1) || (path.get(i).getBelong() != path.get(i + 1).getBelong())) {
                if (clusterHeadCheck != -1) {
                    //If there is only one node in this cluster, make it go to cluster and go back
                    if (clusterHeadCheck == 1) {
                        temp.add(i + shift, path.get(i).getBelong());
                        temp.add(i + shift, path.get(i));
                        shift = shift + 2;
                    } else {
                        //Else simply add the cluster head
                        temp.add(i + shift, path.get(i).getBelong());
                        shift++;
                    }
                }
                clusterHeadCheck = 0;
            }
        }

        return new Path(temp);
    }

    public HashSet<Node> getNeighboors(Node node) {
        HashSet<Node> neighboors = new HashSet<Node>();
        for (int i = 0; i < nodes.length; i++) {
            if (Math.sqrt(Math.pow(node.getX() - nodes[i].getX(), 2) + Math.pow(node.getY() - nodes[i].getY(), 2)) < this.wifiRange) {
                neighboors.add(nodes[i]);
            }
        }
        return neighboors;
    }

    /**
     * This is a set example of Nodes
     * /!\ Only designed for 45 nodes
     * @author Ho
     * @param nodesNumber
     */
    public void setNodes(int numNodes) {
        this.nodes = new Node[numNodes];
        for (int i = 0; i < numNodes; i++) {
            nodes[i] = new Node();
            nodes[i].setId(i);
            nodes[i].setClStatus(Node.STATUS.CLAIMED);
            nodes[i].setX(0);
            nodes[i].setY(0);
        }

        int X = 100;
        int Y = 100;
        int N;
        int currentN = 0;
        boolean fin = false;
        if ((numNodes % 5) == 0) {
            N = numNodes / 5;
        } else {
            N = numNodes / 5 + 1;
        }

        int m = (int) Math.sqrt((double) N);
        if (m * m < N) {
            m = m + 1;
        }

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < m; j++) {
                if (numNodes - 5 > currentN) {
                    nodes[currentN].setX(X);
                    nodes[currentN].setY(Y);
                    nodes[currentN].setBelong(nodes[currentN]);
                    nodes[currentN].setClStatus(Node.STATUS.HEAD);

                    nodes[currentN + 1].setX(X - 100);
                    nodes[currentN + 1].setY(Y);
                    nodes[currentN + 1].setBelong(nodes[currentN]);

                    nodes[currentN + 2].setX(X);
                    nodes[currentN + 2].setY(Y - 100);
                    nodes[currentN + 2].setBelong(nodes[currentN]);

                    nodes[currentN + 3].setX(X);
                    nodes[currentN + 3].setY(Y + 100);
                    nodes[currentN + 3].setBelong(nodes[currentN]);

                    nodes[currentN + 4].setX(X + 100);
                    nodes[currentN + 4].setY(Y);
                    nodes[currentN + 4].setBelong(nodes[currentN]);
                } else {
                    if (numNodes - currentN > 1) {
                        nodes[currentN].setX(X);
                        nodes[currentN].setY(Y);
                        nodes[currentN].setBelong(nodes[currentN]);
                        nodes[currentN].setClStatus(Node.STATUS.HEAD);

                        currentN = currentN + 1;

                        nodes[currentN].setX(X);
                        nodes[currentN].setY(Y - 100);
                        nodes[currentN].setBelong(nodes[currentN - 1]);

                        currentN = currentN + 1;

                        if (currentN == numNodes) {
                            fin = true;
                            break;
                        }

                        nodes[currentN].setX(X - 100);
                        nodes[currentN].setY(Y);
                        nodes[currentN].setBelong(nodes[currentN - 2]);

                        currentN = currentN + 1;

                        if (currentN == numNodes) {
                            fin = true;
                            break;
                        }

                        nodes[currentN].setX(X);
                        nodes[currentN].setY(Y + 100);
                        nodes[currentN].setBelong(nodes[currentN - 3]);

                        currentN = currentN + 1;

                        if (currentN == numNodes) {
                            fin = true;
                            break;
                        }

                        nodes[currentN].setX(X + 100);
                        nodes[currentN].setY(Y);
                        nodes[currentN].setBelong(nodes[currentN - 4]);

                        fin = true;
                        break;
                    } else {
                        nodes[currentN].setX(X);
                        nodes[currentN].setY(Y - 100);
                        nodes[currentN].setBelong(nodes[currentN]);
                        nodes[currentN].setClStatus(Node.STATUS.HEAD);
                        fin = true;
                        break;
                    }
                }
                currentN = currentN + 5;
                if (j == m - 1) {
                    Y = Y + 300;
                    X = 100;
                } else {
                    X = X + 300;
                }
            }
            if (fin == true) {
                break;
            }


        }
    }
    /*
    public ArrayList<Node> shortestPath(Node source, Node destination)
    {
    final int maxi = nodes.length;

    int mini, clusterHeadCheck, shift = 0;

    HashMap<Node, Integer> traveled = new HashMap<Node, Integer>();
    HashMap<Node, Node> previous = new HashMap<Node, Node>();

    Node currentNode;

    ArrayList<Node> path = new ArrayList<Node>();
    ArrayList<Node> todo;
    ArrayList<Node> temp;
    ArrayList<Node> neighboors = new ArrayList<Node>();

    for (int i = 0; i < nodes.length; i++) {
    traveled.put(nodes[i], maxi);
    previous.put(nodes[i], null);
    }
    todo = new ArrayList<Node>(traveled.keySet());
    traveled.put(source, 0);

    while (todo.size() > 0) {
    //Get the minimun
    mini = maxi;
    currentNode = null;
    neighboors.clear();
    for (Node nd : todo) {
    if (traveled.get(nd) <= mini) {
    mini = traveled.get(nd);
    currentNode = nd;
    }
    }

    todo.remove(currentNode);
    for (int i = 0; i < nodes.length; i++) {
    if (Math.sqrt(Math.pow(currentNode.getX() - nodes[i].getX(), 2) + Math.pow(currentNode.getY() - nodes[i].getY(), 2)) < MAXWIFIEMISSION) {
    neighboors.add(nodes[i]);
    }
    }

    for (Node nd : neighboors) {
    //if(currentNode == destination) System.out.println((traveled.get(currentNode) + 1));
    if (traveled.get(nd) > (traveled.get(currentNode) + 1)) {
    traveled.put(nd, traveled.get(currentNode) + 1);
    previous.put(nd, currentNode);
    }
    }
    }
    currentNode = destination;
    while (currentNode != source) {
    currentNode = previous.get(currentNode);
    path.add(currentNode);
    }

    path.add(0, destination);
    temp = new ArrayList<Node>();
    clusterHeadCheck = 0;

    //Reverse the array
    for (int i = path.size() - 1; i >= 0; i--) {
    temp.add(path.get(i));
    }
    path.clear();
    path.addAll(temp);
    //Enforce the cluster head transit
    for (int i = 0; i < path.size(); i++) {
    if (path.get(i).getClStatus() == Node.STATUS.HEAD || clusterHeadCheck == -1) {
    clusterHeadCheck = -1;
    } else {
    clusterHeadCheck++;
    }

    if ((i == path.size() - 1) || (path.get(i).getBelong() != path.get(i + 1).getBelong())) {
    if (clusterHeadCheck != -1) {

    if (clusterHeadCheck == 1) {
    temp.add(i + shift, path.get(i).getBelong());
    temp.add(i + shift, path.get(i));
    shift = shift + 2;
    } else {

    temp.add(i + shift, path.get(i).getBelong());
    shift++;
    }
    }
    clusterHeadCheck = 0;
    }
    }

    path = temp;
    for (int i = (temp.size() - 2); i >= 0; i--) {
    path.add(temp.get(i));
    }
    return path;
    }
     * */
}
